package simpleantserrgroup

import (
	"fmt"
	"log"
	"time"

	"github.com/panjf2000/ants"
	"golang.org/x/sync/errgroup"
)

/*
此模块旨在使用ants和errgroup
先看这两个包的用途、功能和设计思路不同：
1.功能和目的
errgroup: errgroup 主要用于并发任务的执行和错误管理。它允许你并发执行多个任务，并且在遇到第一个错误时取消所有任务并等待所有任务完成。它的设计目的是简化并发任务的错误管理和协作。
适用于以下场景：
	启动多个任务并等待它们完成。
	如果有任务出错，则立即返回错误并取消其他任务。
	可以用于超时、取消任务的场景。

ants：ants 是一个高性能的 goroutine 池库，主要用于管理大量 goroutine 的并发执行。它通过限制最大 goroutine 数量来避免由于过多的 goroutine 引起的系统资源过度消耗。ants 通过线程池的方式来管理并发任务，适合于高并发、高负载的场景。
适用于以下场景：
	高并发任务池的管理。
	需要限制并发数量并防止过多 goroutine 导致性能下降。
	执行大量短时任务时进行性能优化。
2.涉及模式
errgroup：采用了 错误聚合 的设计模式。所有任务都由 errgroup.Group 管理，如果有一个任务失败（返回错误），errgroup 会取消所有任务并传递错误。
	任务可以独立执行，每个任务的错误会通过 errgroup.Group 聚合。
	不直接管理任务的并发数，任务数可以任意增加。

ants：采用了 工作池（Worker Pool）的设计模式。通过设置一个最大 goroutine 数量，ants 控制并发数量，避免由于过多的 goroutine 引起的系统负载过高。
	所有任务都会被放入池中，池内的工作线程会执行任务。
	任务的数量会被限制在最大数量之内，超出的任务会被放入队列等待执行。

3.任务执行和并发管理
errgroup：不限制并发数量，所有任务都是并发执行的，只有当任务出错时，才会停止其他任务。如果有任务失败，errgroup 会立即取消所有任务并返回错误。
	并发数：不限制。
	控制：通过 context 实现任务超时或取消。

ants：通过工作池的方式管理并发任务。池中有固定数量的工作线程，任务会分配给空闲线程。如果工作线程过多，会阻塞直到有线程空闲。
	并发数：有限制，通过 ants.NewPool() 方法创建一个最大并发数的工作池。
	控制：通过工作池管理任务的执行，不会因为过多任务而导致系统资源消耗过大。

4. 错误处理
errgroup：任务执行时如果有错误，errgroup 会立即返回并取消其他任务。它会聚合错误并返回，第一个任务的错误会导致其他任务被取消。
	错误处理：支持错误聚合。
	任务失败处理：任务失败会立即取消其他任务。

ants：ants 本身不处理任务中的错误。任务中的错误处理完全依赖于任务内部的逻辑。如果任务失败，ants 只是执行任务，并不会主动停止其他任务或返回错误。
	错误处理：没有内建的错误聚合机制，错误需要在任务内部处理。

5. 性能
errgroup：因为它不限制并发数，所以适合小规模的任务管理，并且重点在于错误处理和任务的聚合。适用于错误管理要求高的场景。
	性能：适用于少量并发任务，任务数量较少时表现较好。

ants：专门优化了高并发场景，能高效地管理大量并发任务。适用于任务数较多且每个任务执行时间较短的场景。
	性能：能够处理大量的并发任务，避免过多的 goroutine 引起性能瓶颈。适合大规模并发任务。

6. 使用场景
errgroup：
	适用于并发执行任务，且任务之间需要错误聚合的场景。
	适用于任务数不多，但对任务的错误处理要求较高的场景。
	适用于需要控制任务取消、超时的场景。
ants：
	适用于高并发的场景，尤其是有大量短时间执行的任务。
	适用于避免过多 goroutine 导致性能下降的场景。
	适用于任务执行较频繁且任务数目较大的场景。

总结：
errgroup：专注于并发任务的错误处理和取消，适合错误管理要求较高的场景。
ants：适用于高并发任务池的场景，能够限制并发 goroutine 数量，防止系统资源过度消耗。
*/

/*
现在模拟一个场景：
如果现在既有高并发场景，即goroutine非常多，但也要做错误处理与协作，即一个goroutinue报错，取消其他协程，目的使用ants和errgroup都使用
*/
func Do() {
	// 创建一个包含 5 个并发线程的工作池
	pool, err := ants.NewPool(5)
	if err != nil {
		log.Fatalf("Failed to create goroutine pool: %v\n", err)
	}
	defer pool.Release()

	var g errgroup.Group

	// 错误通道，用于传递任务错误
	errChan := make(chan error, 1)

	// 启动多个任务
	for i := 0; i < 10; i++ {
		i := i // 避免闭包捕获错误
		g.Go(func() error {
			// 模拟业务逻辑
			return monitorTask(pool, i, errChan)
		})
	}

	// 监听错误和完成状态
	go func() {
		select {
		case err := <-errChan: // 如果有任务出错
			log.Printf("Error occurred: %v\n", err)
			// 可以在这里主动取消任务
			// pool.Release() 释放goroutine池资源
		}
	}()

	// 等待所有任务完成或出错
	if err := g.Wait(); err != nil {
		log.Printf("Error: %v\n", err)
	} else {
		log.Println("All tasks completed successfully.")
	}
}

// 模拟任务执行的函数
func monitorTask(pool *ants.Pool, taskId int, errChan chan error) error {
	// 模拟任务执行时间
	time.Sleep(time.Duration(taskId%3) * time.Second)

	// 如果任务出现错误，发送错误到 errChan
	if taskId == 5 { // 模拟任务 5 出错
		errChan <- fmt.Errorf("task %d encountered an error", taskId)
		return fmt.Errorf("task %d failed", taskId)
	}

	// 正常完成任务
	fmt.Printf("Task %d completed successfully\n", taskId)
	return nil
}
